﻿namespace MemoryTest_Communications.Model
{
    using System;
    using System.Device.Location;
    using WP7Contrib.Common;

    /// <summary>
    /// The radius.
    /// </summary>
    public class Radius : BaseModel, IEquatable<Radius>
    {
        #region Constants and Fields

        public static readonly Radius Unknown = new Radius(GeoCoordinate.Unknown, 0, new DistanceUnit());

        /// <summary>
        /// The _centre.
        /// </summary>
        private readonly GeoCoordinate _centre;

        /// <summary>
        /// The _distance.
        /// </summary>
        private readonly decimal _distance;

        /// <summary>
        /// The _distance unit.
        /// </summary>
        private readonly DistanceUnit _distanceUnit;

        #endregion

        #region Constructors and Destructors

        public Radius()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Radius"/> struct.
        /// </summary>
        /// <param name="centre">
        /// The centre.
        /// </param>
        /// <param name="distance">
        /// The distance.
        /// </param>
        /// <param name="unit">
        /// The unit.
        /// </param>
        public Radius(GeoCoordinate centre, decimal distance, DistanceUnit unit)
        {
            this._centre = centre;
            this._distance = distance;
            this._distanceUnit = unit;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets Distance.
        /// </summary>
        public decimal Distance
        {
            get
            {
                return this._distance;
            }
        }

        /// <summary>
        /// Gets DistanceUnit.
        /// </summary>
        public DistanceUnit DistanceUnit
        {
            get
            {
                return this._distanceUnit;
            }
        }

        /// <summary>
        /// Gets Latitude.
        /// </summary>
        public double Latitude
        {
            get
            {
                return this._centre.Latitude;
            }
        }

        /// <summary>
        /// Gets Longitude.
        /// </summary>
        public double Longitude
        {
            get
            {
                return this._centre.Longitude;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// The has value.
        /// </summary>
        /// <returns>
        /// The has value.
        /// </returns>
        public bool HasValue()
        {
            return this.Latitude > 0 && this.Longitude > 0;
        }

        #endregion

        public bool Equals(Radius radius)
        {
            if (Distance != radius.Distance)
                return false;

            if (DistanceUnit != radius.DistanceUnit)
                return false;

            if (double.IsNaN(Latitude))
            {
                if (!double.IsNaN(radius.Latitude))
                    return false;
            }
            else if (Latitude != radius.Latitude)
                return false;

            if (double.IsNaN(Longitude))
            {
                if (!double.IsNaN(radius.Longitude))
                    return false;
            }
            else if (Longitude != radius.Longitude)
                return false;

            return true;
        }

        public override bool Equals(Object obj)
        {
            if (ReferenceEquals(null, obj))
                return false;

            return obj is Radius && Equals((Radius)obj);
        }

        public override int GetHashCode()
        {
            return CombineHashCodes(new object[] { Distance, DistanceUnit, Latitude, Longitude });
        }

        public static bool operator ==(Radius r1, Radius r2)
        {
            return Equals(r1, r2);
        }

        public static bool operator !=(Radius r1, Radius s2)
        {
            return (!Equals(r1, s2));
        }

        private int CombineHashCodes(params object[] objects)
        {
            var hash = 0;

            foreach (object t in objects)
            {
                hash = (hash << 5) + hash;
                hash ^= GetEntryHash(t);
            }

            return hash;
        }

        private int GetEntryHash(object entry)
        {
            var entryHash = 0x61E04917; // slurped from .Net runtime internals...

            if (entry != null)
            {
                var subObjects = entry as object[];
                entryHash = subObjects != null ? CombineHashCodes(subObjects) : entry.GetHashCode();
            }

            return entryHash;
        }
    }
}